﻿namespace Spectral
{
	using System;
	using Spectral.Operations;

	/// <summary>
	/// Represents a 2x2 matrix.
	/// </summary>
	public struct Matrix2x2<T> : IEquatable<Matrix2x2<T>>
		where T : struct, IEquatable<T>, IComparable<T>
	{
		/// <summary>
		/// An identity matrix.
		/// </summary>
		public static readonly Matrix2x2<T> Identity = new Matrix2x2<T>(Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>());

		/// <summary>
		/// The element at [0, 0].
		/// </summary>
		public readonly T M11;
		
		/// <summary>
		/// The element at [0, 1].
		/// </summary>
		public readonly T M12;
		
		/// <summary>
		/// The element at [1, 0].
		/// </summary>
		public readonly T M21;
		
		/// <summary>
		/// The element at [1, 1].
		/// </summary>
		public readonly T M22;
		
		/// <summary>
		/// Initializes a new instance of the <see cref="Matrix2x2&lt;T&gt;"/> struct.
		/// </summary>
		/// <param name="m11">The element at [0, 0].</param>
		/// <param name="m12">The element at [0, 1].</param>
		/// <param name="m21">The element at [1, 0].</param>
		/// <param name="m22">The element at [1, 1].</param>
		public Matrix2x2(T m11, T m12, T m21, T m22)
		{
			M11 = m11;
			M12 = m12;
			M21 = m21;
			M22 = m22;
		}

		/// <summary>
		/// Adds the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<T> Add(Matrix2x2<T> value)
		{
			return new Matrix2x2<T>(M11.Add(value.M11), M12.Add(value.M12), M21.Add(value.M21), M22.Add(value.M22));
		}

		/// <summary>
		/// Subtract the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<T> Subtract(Matrix2x2<T> value)
		{
			return new Matrix2x2<T>(M11.Subtract(value.M11), M12.Subtract(value.M12), M21.Subtract(value.M21), M22.Subtract(value.M22));
		}

		/// <summary>
		/// Multiplies by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<T> Multiply(Matrix2x2<T> value)
		{
			return new Matrix2x2<T>(
				(M11.Multiply(value.M11)).Add(M12.Multiply(value.M21)),				
				(M11.Multiply(value.M12)).Add(M12.Multiply(value.M22)),				
				(M21.Multiply(value.M11)).Add(M22.Multiply(value.M21)),				
				(M21.Multiply(value.M12)).Add(M22.Multiply(value.M22)));				
		}

		/// <summary>
		/// Multiplies by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<T> Multiply(T value)
		{
			return new Matrix2x2<T>(M11.Multiply(value), M12.Multiply(value), M21.Multiply(value), M22.Multiply(value));
		}

		/// <summary>
		/// Divides by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<T> Divide(Matrix2x2<T> value)
		{
			return new Matrix2x2<T>(M11.Divide(value.M11), M12.Divide(value.M12), M21.Divide(value.M21), M22.Divide(value.M22));
		}

		/// <summary>
		/// Divides by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<T> Divide(T value)
		{
			return new Matrix2x2<T>(M11.Divide(value), M12.Divide(value), M21.Divide(value), M22.Divide(value));
		}

		/// <summary>
		/// Negates this matrix.
		/// </summary>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<T> Negate()
		{
			return new Matrix2x2<T>(M11.Negate(), M12.Negate(), M21.Negate(), M22.Negate());
		}

		/// <summary>
		/// Calculates the determinant of this matrix.
		/// </summary>
		/// <returns>The determinant.</returns>
		public T Determinant()
		{
			return M11.Multiply(M22).Subtract(M12.Multiply(M21));
		}
		
		/// <summary>
		/// Calculates the inverse of this matrix.
		/// </summary>
		/// <returns>The inverse.</returns>
		public Matrix2x2<T> Inverse()
		{
			return new Matrix2x2<T>(M22, M12.Negate(), M21.Negate(), M11).Divide(Determinant());
		}
		
		/// <summary>
		/// Calculates the transpose of this matrix.
		/// </summary>
		/// <returns>The transpose.</returns>
		public Matrix2x2<T> Transpose()
		{
			return new Matrix2x2<T>(M11, M21, M12, M22);
		}
		
		/// <summary>
		/// Casts this matrix to another matrix with a different element type.
		/// </summary>
		/// <returns>The result of the operation.</returns>
		public Matrix2x2<U> Cast<U>()
			where U : struct, IEquatable<U>, IComparable<U>
		{
			return new Matrix2x2<U>((U)Convert.ChangeType(M11, typeof(U)), (U)Convert.ChangeType(M12, typeof(U)), (U)Convert.ChangeType(M21, typeof(U)), (U)Convert.ChangeType(M22, typeof(U)));
		}

		/// <summary>
		/// Adds two matrices together.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix2x2<T> operator +(Matrix2x2<T> left, Matrix2x2<T> right)
		{
			return left.Add(right);
		}
		
		/// <summary>
		/// Subtracts one matrix from another.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix2x2<T> operator -(Matrix2x2<T> left, Matrix2x2<T> right)
		{
			return left.Subtract(right);
		}
		
		/// <summary>
		/// Multiplies two matrices together.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix2x2<T> operator *(Matrix2x2<T> left, Matrix2x2<T> right)
		{
			return left.Multiply(right);
		}
		
		/// <summary>
		/// Multiplies a matrix by a scalar.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix2x2<T> operator *(Matrix2x2<T> left, T right)
		{
			return left.Multiply(right);
		}
		
		/// <summary>
		/// Divides a matrix by another.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix2x2<T> operator /(Matrix2x2<T> left, Matrix2x2<T> right)
		{
			return left.Divide(right);
		}
		
		/// <summary>
		/// Divides a matrix by a scalar.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix2x2<T> operator /(Matrix2x2<T> left, T right)
		{
			return left.Divide(right);
		}
		
		/// <summary>
		/// Negates a matrix.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix2x2<T> operator -(Matrix2x2<T> value)
		{
			return value.Negate();
		}

		/// <summary>
		/// Determines if two matrices are equal.
		/// </summary>
		/// <param name="left">The left side of the comparison.</param>
		/// <param name="right">The right side of the comparison.</param>
		/// <returns>The result of the comparison.</returns>
		public static bool operator ==(Matrix2x2<T> left, Matrix2x2<T> right)
		{
			return left.Equals(right);
		}
		
		/// <summary>
		/// Determines if two matrices are not equal.
		/// </summary>
		/// <param name="left">The left side of the comparison.</param>
		/// <param name="right">The right side of the comparison.</param>
		/// <returns>The result of the comparison.</returns>
		public static bool operator !=(Matrix2x2<T> left, Matrix2x2<T> right)
		{
			return !left.Equals(right);
		}
		
		/// <summary>
		/// Indicates whether the current object is equal to another object of the same type.
		/// </summary>
		/// <param name="other">An object to compare with this object.</param>
		/// <returns>
		/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
		/// </returns>
		public bool Equals(Matrix2x2<T> other)
		{
			return M11.Equals(other.M11) && M12.Equals(other.M12) && M21.Equals(other.M21) && M22.Equals(other.M22);
		}
		
		/// <summary>
		/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
		/// <returns>
		///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		public override bool Equals(object obj)
		{
			return obj is Matrix2x2<T> ? Equals((Matrix2x2<T>)obj) : false;
		}
		
		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode()
		{
			return M11.GetHashCode() ^ M12.GetHashCode() ^ M21.GetHashCode() ^ M22.GetHashCode();
		}
		
		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public override string ToString()
		{
			return "M11: " + M11.ToString() + ", " + "M12: " + M12.ToString() + ", " + "M21: " + M21.ToString() + ", " + "M22: " + M22.ToString();
		}
	}

	/// <summary>
	/// Represents a 3x3 matrix.
	/// </summary>
	public struct Matrix3x3<T> : IEquatable<Matrix3x3<T>>
		where T : struct, IEquatable<T>, IComparable<T>
	{
		/// <summary>
		/// An identity matrix.
		/// </summary>
		public static readonly Matrix3x3<T> Identity = new Matrix3x3<T>(Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>());

		/// <summary>
		/// The element at [0, 0].
		/// </summary>
		public readonly T M11;
		
		/// <summary>
		/// The element at [0, 1].
		/// </summary>
		public readonly T M12;
		
		/// <summary>
		/// The element at [0, 2].
		/// </summary>
		public readonly T M13;
		
		/// <summary>
		/// The element at [1, 0].
		/// </summary>
		public readonly T M21;
		
		/// <summary>
		/// The element at [1, 1].
		/// </summary>
		public readonly T M22;
		
		/// <summary>
		/// The element at [1, 2].
		/// </summary>
		public readonly T M23;
		
		/// <summary>
		/// The element at [2, 0].
		/// </summary>
		public readonly T M31;
		
		/// <summary>
		/// The element at [2, 1].
		/// </summary>
		public readonly T M32;
		
		/// <summary>
		/// The element at [2, 2].
		/// </summary>
		public readonly T M33;
		
		/// <summary>
		/// Initializes a new instance of the <see cref="Matrix2x2&lt;T&gt;"/> struct.
		/// </summary>
		/// <param name="m11">The element at [0, 0].</param>
		/// <param name="m12">The element at [0, 1].</param>
		/// <param name="m13">The element at [0, 2].</param>
		/// <param name="m21">The element at [1, 0].</param>
		/// <param name="m22">The element at [1, 1].</param>
		/// <param name="m23">The element at [1, 2].</param>
		/// <param name="m31">The element at [2, 0].</param>
		/// <param name="m32">The element at [2, 1].</param>
		/// <param name="m33">The element at [2, 2].</param>
		public Matrix3x3(T m11, T m12, T m13, T m21, T m22, T m23, T m31, T m32, T m33)
		{
			M11 = m11;
			M12 = m12;
			M13 = m13;
			M21 = m21;
			M22 = m22;
			M23 = m23;
			M31 = m31;
			M32 = m32;
			M33 = m33;
		}

		/// <summary>
		/// Adds the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<T> Add(Matrix3x3<T> value)
		{
			return new Matrix3x3<T>(M11.Add(value.M11), M12.Add(value.M12), M13.Add(value.M13), M21.Add(value.M21), M22.Add(value.M22), M23.Add(value.M23), M31.Add(value.M31), M32.Add(value.M32), M33.Add(value.M33));
		}

		/// <summary>
		/// Subtract the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<T> Subtract(Matrix3x3<T> value)
		{
			return new Matrix3x3<T>(M11.Subtract(value.M11), M12.Subtract(value.M12), M13.Subtract(value.M13), M21.Subtract(value.M21), M22.Subtract(value.M22), M23.Subtract(value.M23), M31.Subtract(value.M31), M32.Subtract(value.M32), M33.Subtract(value.M33));
		}

		/// <summary>
		/// Multiplies by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<T> Multiply(Matrix3x3<T> value)
		{
			return new Matrix3x3<T>(
				(M11.Multiply(value.M11)).Add(M12.Multiply(value.M21)).Add(M13.Multiply(value.M31)),				
				(M11.Multiply(value.M12)).Add(M12.Multiply(value.M22)).Add(M13.Multiply(value.M32)),				
				(M11.Multiply(value.M13)).Add(M12.Multiply(value.M23)).Add(M13.Multiply(value.M33)),				
				(M21.Multiply(value.M11)).Add(M22.Multiply(value.M21)).Add(M23.Multiply(value.M31)),				
				(M21.Multiply(value.M12)).Add(M22.Multiply(value.M22)).Add(M23.Multiply(value.M32)),				
				(M21.Multiply(value.M13)).Add(M22.Multiply(value.M23)).Add(M23.Multiply(value.M33)),				
				(M31.Multiply(value.M11)).Add(M32.Multiply(value.M21)).Add(M33.Multiply(value.M31)),				
				(M31.Multiply(value.M12)).Add(M32.Multiply(value.M22)).Add(M33.Multiply(value.M32)),				
				(M31.Multiply(value.M13)).Add(M32.Multiply(value.M23)).Add(M33.Multiply(value.M33)));				
		}

		/// <summary>
		/// Multiplies by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<T> Multiply(T value)
		{
			return new Matrix3x3<T>(M11.Multiply(value), M12.Multiply(value), M13.Multiply(value), M21.Multiply(value), M22.Multiply(value), M23.Multiply(value), M31.Multiply(value), M32.Multiply(value), M33.Multiply(value));
		}

		/// <summary>
		/// Divides by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<T> Divide(Matrix3x3<T> value)
		{
			return new Matrix3x3<T>(M11.Divide(value.M11), M12.Divide(value.M12), M13.Divide(value.M13), M21.Divide(value.M21), M22.Divide(value.M22), M23.Divide(value.M23), M31.Divide(value.M31), M32.Divide(value.M32), M33.Divide(value.M33));
		}

		/// <summary>
		/// Divides by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<T> Divide(T value)
		{
			return new Matrix3x3<T>(M11.Divide(value), M12.Divide(value), M13.Divide(value), M21.Divide(value), M22.Divide(value), M23.Divide(value), M31.Divide(value), M32.Divide(value), M33.Divide(value));
		}

		/// <summary>
		/// Negates this matrix.
		/// </summary>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<T> Negate()
		{
			return new Matrix3x3<T>(M11.Negate(), M12.Negate(), M13.Negate(), M21.Negate(), M22.Negate(), M23.Negate(), M31.Negate(), M32.Negate(), M33.Negate());
		}

		/// <summary>
		/// Calculates the determinant of this matrix.
		/// </summary>
		/// <returns>The determinant.</returns>
		public T Determinant()
		{
			return (new Matrix2x2<T>(M22, M23, M32, M33).Determinant().Multiply(M11)).Add(new Matrix2x2<T>(M21, M23, M31, M33).Determinant().Multiply(M12).Negate()).Add(new Matrix2x2<T>(M21, M22, M31, M32).Determinant().Multiply(M13));
		}
		
		/// <summary>
		/// Calculates the inverse of this matrix.
		/// </summary>
		/// <returns>The inverse.</returns>
		public Matrix3x3<T> Inverse()
		{
			return new Matrix3x3<T>((new Matrix2x2<T>(M22, M23, M32, M33).Determinant().Multiply(M11)), (new Matrix2x2<T>(M12, M13, M32, M33).Determinant().Multiply(M12).Negate()), (new Matrix2x2<T>(M12, M13, M22, M23).Determinant().Multiply(M13)), (new Matrix2x2<T>(M21, M23, M31, M33).Determinant().Multiply(M21).Negate()), (new Matrix2x2<T>(M11, M13, M31, M33).Determinant().Multiply(M22)), (new Matrix2x2<T>(M11, M13, M21, M23).Determinant().Multiply(M23).Negate()), (new Matrix2x2<T>(M21, M22, M31, M32).Determinant().Multiply(M31)), (new Matrix2x2<T>(M11, M12, M31, M32).Determinant().Multiply(M32).Negate()), (new Matrix2x2<T>(M11, M12, M21, M22).Determinant().Multiply(M33))).Divide(Determinant());

		}
		
		/// <summary>
		/// Calculates the transpose of this matrix.
		/// </summary>
		/// <returns>The transpose.</returns>
		public Matrix3x3<T> Transpose()
		{
			return new Matrix3x3<T>(M11, M21, M31, M12, M22, M32, M13, M23, M33);
		}
		
		/// <summary>
		/// Casts this matrix to another matrix with a different element type.
		/// </summary>
		/// <returns>The result of the operation.</returns>
		public Matrix3x3<U> Cast<U>()
			where U : struct, IEquatable<U>, IComparable<U>
		{
			return new Matrix3x3<U>((U)Convert.ChangeType(M11, typeof(U)), (U)Convert.ChangeType(M12, typeof(U)), (U)Convert.ChangeType(M13, typeof(U)), (U)Convert.ChangeType(M21, typeof(U)), (U)Convert.ChangeType(M22, typeof(U)), (U)Convert.ChangeType(M23, typeof(U)), (U)Convert.ChangeType(M31, typeof(U)), (U)Convert.ChangeType(M32, typeof(U)), (U)Convert.ChangeType(M33, typeof(U)));
		}

		/// <summary>
		/// Adds two matrices together.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix3x3<T> operator +(Matrix3x3<T> left, Matrix3x3<T> right)
		{
			return left.Add(right);
		}
		
		/// <summary>
		/// Subtracts one matrix from another.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix3x3<T> operator -(Matrix3x3<T> left, Matrix3x3<T> right)
		{
			return left.Subtract(right);
		}
		
		/// <summary>
		/// Multiplies two matrices together.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix3x3<T> operator *(Matrix3x3<T> left, Matrix3x3<T> right)
		{
			return left.Multiply(right);
		}
		
		/// <summary>
		/// Multiplies a matrix by a scalar.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix3x3<T> operator *(Matrix3x3<T> left, T right)
		{
			return left.Multiply(right);
		}
		
		/// <summary>
		/// Divides a matrix by another.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix3x3<T> operator /(Matrix3x3<T> left, Matrix3x3<T> right)
		{
			return left.Divide(right);
		}
		
		/// <summary>
		/// Divides a matrix by a scalar.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix3x3<T> operator /(Matrix3x3<T> left, T right)
		{
			return left.Divide(right);
		}
		
		/// <summary>
		/// Negates a matrix.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix3x3<T> operator -(Matrix3x3<T> value)
		{
			return value.Negate();
		}

		/// <summary>
		/// Determines if two matrices are equal.
		/// </summary>
		/// <param name="left">The left side of the comparison.</param>
		/// <param name="right">The right side of the comparison.</param>
		/// <returns>The result of the comparison.</returns>
		public static bool operator ==(Matrix3x3<T> left, Matrix3x3<T> right)
		{
			return left.Equals(right);
		}
		
		/// <summary>
		/// Determines if two matrices are not equal.
		/// </summary>
		/// <param name="left">The left side of the comparison.</param>
		/// <param name="right">The right side of the comparison.</param>
		/// <returns>The result of the comparison.</returns>
		public static bool operator !=(Matrix3x3<T> left, Matrix3x3<T> right)
		{
			return !left.Equals(right);
		}
		
		/// <summary>
		/// Indicates whether the current object is equal to another object of the same type.
		/// </summary>
		/// <param name="other">An object to compare with this object.</param>
		/// <returns>
		/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
		/// </returns>
		public bool Equals(Matrix3x3<T> other)
		{
			return M11.Equals(other.M11) && M12.Equals(other.M12) && M13.Equals(other.M13) && M21.Equals(other.M21) && M22.Equals(other.M22) && M23.Equals(other.M23) && M31.Equals(other.M31) && M32.Equals(other.M32) && M33.Equals(other.M33);
		}
		
		/// <summary>
		/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
		/// <returns>
		///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		public override bool Equals(object obj)
		{
			return obj is Matrix3x3<T> ? Equals((Matrix3x3<T>)obj) : false;
		}
		
		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode()
		{
			return M11.GetHashCode() ^ M12.GetHashCode() ^ M13.GetHashCode() ^ M21.GetHashCode() ^ M22.GetHashCode() ^ M23.GetHashCode() ^ M31.GetHashCode() ^ M32.GetHashCode() ^ M33.GetHashCode();
		}
		
		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public override string ToString()
		{
			return "M11: " + M11.ToString() + ", " + "M12: " + M12.ToString() + ", " + "M13: " + M13.ToString() + ", " + "M21: " + M21.ToString() + ", " + "M22: " + M22.ToString() + ", " + "M23: " + M23.ToString() + ", " + "M31: " + M31.ToString() + ", " + "M32: " + M32.ToString() + ", " + "M33: " + M33.ToString();
		}
	}

	/// <summary>
	/// Represents a 4x4 matrix.
	/// </summary>
	public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>>
		where T : struct, IEquatable<T>, IComparable<T>
	{
		/// <summary>
		/// An identity matrix.
		/// </summary>
		public static readonly Matrix4x4<T> Identity = new Matrix4x4<T>(Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>());

		/// <summary>
		/// The element at [0, 0].
		/// </summary>
		public readonly T M11;
		
		/// <summary>
		/// The element at [0, 1].
		/// </summary>
		public readonly T M12;
		
		/// <summary>
		/// The element at [0, 2].
		/// </summary>
		public readonly T M13;
		
		/// <summary>
		/// The element at [0, 3].
		/// </summary>
		public readonly T M14;
		
		/// <summary>
		/// The element at [1, 0].
		/// </summary>
		public readonly T M21;
		
		/// <summary>
		/// The element at [1, 1].
		/// </summary>
		public readonly T M22;
		
		/// <summary>
		/// The element at [1, 2].
		/// </summary>
		public readonly T M23;
		
		/// <summary>
		/// The element at [1, 3].
		/// </summary>
		public readonly T M24;
		
		/// <summary>
		/// The element at [2, 0].
		/// </summary>
		public readonly T M31;
		
		/// <summary>
		/// The element at [2, 1].
		/// </summary>
		public readonly T M32;
		
		/// <summary>
		/// The element at [2, 2].
		/// </summary>
		public readonly T M33;
		
		/// <summary>
		/// The element at [2, 3].
		/// </summary>
		public readonly T M34;
		
		/// <summary>
		/// The element at [3, 0].
		/// </summary>
		public readonly T M41;
		
		/// <summary>
		/// The element at [3, 1].
		/// </summary>
		public readonly T M42;
		
		/// <summary>
		/// The element at [3, 2].
		/// </summary>
		public readonly T M43;
		
		/// <summary>
		/// The element at [3, 3].
		/// </summary>
		public readonly T M44;
		
		/// <summary>
		/// Initializes a new instance of the <see cref="Matrix2x2&lt;T&gt;"/> struct.
		/// </summary>
		/// <param name="m11">The element at [0, 0].</param>
		/// <param name="m12">The element at [0, 1].</param>
		/// <param name="m13">The element at [0, 2].</param>
		/// <param name="m14">The element at [0, 3].</param>
		/// <param name="m21">The element at [1, 0].</param>
		/// <param name="m22">The element at [1, 1].</param>
		/// <param name="m23">The element at [1, 2].</param>
		/// <param name="m24">The element at [1, 3].</param>
		/// <param name="m31">The element at [2, 0].</param>
		/// <param name="m32">The element at [2, 1].</param>
		/// <param name="m33">The element at [2, 2].</param>
		/// <param name="m34">The element at [2, 3].</param>
		/// <param name="m41">The element at [3, 0].</param>
		/// <param name="m42">The element at [3, 1].</param>
		/// <param name="m43">The element at [3, 2].</param>
		/// <param name="m44">The element at [3, 3].</param>
		public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44)
		{
			M11 = m11;
			M12 = m12;
			M13 = m13;
			M14 = m14;
			M21 = m21;
			M22 = m22;
			M23 = m23;
			M24 = m24;
			M31 = m31;
			M32 = m32;
			M33 = m33;
			M34 = m34;
			M41 = m41;
			M42 = m42;
			M43 = m43;
			M44 = m44;
		}

		/// <summary>
		/// Adds the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<T> Add(Matrix4x4<T> value)
		{
			return new Matrix4x4<T>(M11.Add(value.M11), M12.Add(value.M12), M13.Add(value.M13), M14.Add(value.M14), M21.Add(value.M21), M22.Add(value.M22), M23.Add(value.M23), M24.Add(value.M24), M31.Add(value.M31), M32.Add(value.M32), M33.Add(value.M33), M34.Add(value.M34), M41.Add(value.M41), M42.Add(value.M42), M43.Add(value.M43), M44.Add(value.M44));
		}

		/// <summary>
		/// Subtract the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<T> Subtract(Matrix4x4<T> value)
		{
			return new Matrix4x4<T>(M11.Subtract(value.M11), M12.Subtract(value.M12), M13.Subtract(value.M13), M14.Subtract(value.M14), M21.Subtract(value.M21), M22.Subtract(value.M22), M23.Subtract(value.M23), M24.Subtract(value.M24), M31.Subtract(value.M31), M32.Subtract(value.M32), M33.Subtract(value.M33), M34.Subtract(value.M34), M41.Subtract(value.M41), M42.Subtract(value.M42), M43.Subtract(value.M43), M44.Subtract(value.M44));
		}

		/// <summary>
		/// Multiplies by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<T> Multiply(Matrix4x4<T> value)
		{
			return new Matrix4x4<T>(
				(M11.Multiply(value.M11)).Add(M12.Multiply(value.M21)).Add(M13.Multiply(value.M31)).Add(M14.Multiply(value.M41)),				
				(M11.Multiply(value.M12)).Add(M12.Multiply(value.M22)).Add(M13.Multiply(value.M32)).Add(M14.Multiply(value.M42)),				
				(M11.Multiply(value.M13)).Add(M12.Multiply(value.M23)).Add(M13.Multiply(value.M33)).Add(M14.Multiply(value.M43)),				
				(M11.Multiply(value.M14)).Add(M12.Multiply(value.M24)).Add(M13.Multiply(value.M34)).Add(M14.Multiply(value.M44)),				
				(M21.Multiply(value.M11)).Add(M22.Multiply(value.M21)).Add(M23.Multiply(value.M31)).Add(M24.Multiply(value.M41)),				
				(M21.Multiply(value.M12)).Add(M22.Multiply(value.M22)).Add(M23.Multiply(value.M32)).Add(M24.Multiply(value.M42)),				
				(M21.Multiply(value.M13)).Add(M22.Multiply(value.M23)).Add(M23.Multiply(value.M33)).Add(M24.Multiply(value.M43)),				
				(M21.Multiply(value.M14)).Add(M22.Multiply(value.M24)).Add(M23.Multiply(value.M34)).Add(M24.Multiply(value.M44)),				
				(M31.Multiply(value.M11)).Add(M32.Multiply(value.M21)).Add(M33.Multiply(value.M31)).Add(M34.Multiply(value.M41)),				
				(M31.Multiply(value.M12)).Add(M32.Multiply(value.M22)).Add(M33.Multiply(value.M32)).Add(M34.Multiply(value.M42)),				
				(M31.Multiply(value.M13)).Add(M32.Multiply(value.M23)).Add(M33.Multiply(value.M33)).Add(M34.Multiply(value.M43)),				
				(M31.Multiply(value.M14)).Add(M32.Multiply(value.M24)).Add(M33.Multiply(value.M34)).Add(M34.Multiply(value.M44)),				
				(M41.Multiply(value.M11)).Add(M42.Multiply(value.M21)).Add(M43.Multiply(value.M31)).Add(M44.Multiply(value.M41)),				
				(M41.Multiply(value.M12)).Add(M42.Multiply(value.M22)).Add(M43.Multiply(value.M32)).Add(M44.Multiply(value.M42)),				
				(M41.Multiply(value.M13)).Add(M42.Multiply(value.M23)).Add(M43.Multiply(value.M33)).Add(M44.Multiply(value.M43)),				
				(M41.Multiply(value.M14)).Add(M42.Multiply(value.M24)).Add(M43.Multiply(value.M34)).Add(M44.Multiply(value.M44)));				
		}

		/// <summary>
		/// Multiplies by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<T> Multiply(T value)
		{
			return new Matrix4x4<T>(M11.Multiply(value), M12.Multiply(value), M13.Multiply(value), M14.Multiply(value), M21.Multiply(value), M22.Multiply(value), M23.Multiply(value), M24.Multiply(value), M31.Multiply(value), M32.Multiply(value), M33.Multiply(value), M34.Multiply(value), M41.Multiply(value), M42.Multiply(value), M43.Multiply(value), M44.Multiply(value));
		}

		/// <summary>
		/// Divides by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<T> Divide(Matrix4x4<T> value)
		{
			return new Matrix4x4<T>(M11.Divide(value.M11), M12.Divide(value.M12), M13.Divide(value.M13), M14.Divide(value.M14), M21.Divide(value.M21), M22.Divide(value.M22), M23.Divide(value.M23), M24.Divide(value.M24), M31.Divide(value.M31), M32.Divide(value.M32), M33.Divide(value.M33), M34.Divide(value.M34), M41.Divide(value.M41), M42.Divide(value.M42), M43.Divide(value.M43), M44.Divide(value.M44));
		}

		/// <summary>
		/// Divides by the specified value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<T> Divide(T value)
		{
			return new Matrix4x4<T>(M11.Divide(value), M12.Divide(value), M13.Divide(value), M14.Divide(value), M21.Divide(value), M22.Divide(value), M23.Divide(value), M24.Divide(value), M31.Divide(value), M32.Divide(value), M33.Divide(value), M34.Divide(value), M41.Divide(value), M42.Divide(value), M43.Divide(value), M44.Divide(value));
		}

		/// <summary>
		/// Negates this matrix.
		/// </summary>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<T> Negate()
		{
			return new Matrix4x4<T>(M11.Negate(), M12.Negate(), M13.Negate(), M14.Negate(), M21.Negate(), M22.Negate(), M23.Negate(), M24.Negate(), M31.Negate(), M32.Negate(), M33.Negate(), M34.Negate(), M41.Negate(), M42.Negate(), M43.Negate(), M44.Negate());
		}

		/// <summary>
		/// Calculates the determinant of this matrix.
		/// </summary>
		/// <returns>The determinant.</returns>
		public T Determinant()
		{
			return (new Matrix3x3<T>(M22, M23, M24, M32, M33, M34, M42, M43, M44).Determinant().Multiply(M11)).Add(new Matrix3x3<T>(M21, M23, M24, M31, M33, M34, M41, M43, M44).Determinant().Multiply(M12).Negate()).Add(new Matrix3x3<T>(M21, M22, M24, M31, M32, M34, M41, M42, M44).Determinant().Multiply(M13)).Add(new Matrix3x3<T>(M21, M22, M23, M31, M32, M33, M41, M42, M43).Determinant().Multiply(M14).Negate());
		}
		
		/// <summary>
		/// Calculates the inverse of this matrix.
		/// </summary>
		/// <returns>The inverse.</returns>
		public Matrix4x4<T> Inverse()
		{
			return new Matrix4x4<T>((new Matrix3x3<T>(M22, M23, M24, M32, M33, M34, M42, M43, M44).Determinant().Multiply(M11)), (new Matrix3x3<T>(M12, M13, M14, M32, M33, M34, M42, M43, M44).Determinant().Multiply(M12).Negate()), (new Matrix3x3<T>(M12, M13, M14, M22, M23, M24, M42, M43, M44).Determinant().Multiply(M13)), (new Matrix3x3<T>(M12, M13, M14, M22, M23, M24, M32, M33, M34).Determinant().Multiply(M14).Negate()), (new Matrix3x3<T>(M21, M23, M24, M31, M33, M34, M41, M43, M44).Determinant().Multiply(M21).Negate()), (new Matrix3x3<T>(M11, M13, M14, M31, M33, M34, M41, M43, M44).Determinant().Multiply(M22)), (new Matrix3x3<T>(M11, M13, M14, M21, M23, M24, M41, M43, M44).Determinant().Multiply(M23).Negate()), (new Matrix3x3<T>(M11, M13, M14, M21, M23, M24, M31, M33, M34).Determinant().Multiply(M24)), (new Matrix3x3<T>(M21, M22, M24, M31, M32, M34, M41, M42, M44).Determinant().Multiply(M31)), (new Matrix3x3<T>(M11, M12, M14, M31, M32, M34, M41, M42, M44).Determinant().Multiply(M32).Negate()), (new Matrix3x3<T>(M11, M12, M14, M21, M22, M24, M41, M42, M44).Determinant().Multiply(M33)), (new Matrix3x3<T>(M11, M12, M14, M21, M22, M24, M31, M32, M34).Determinant().Multiply(M34).Negate()), (new Matrix3x3<T>(M21, M22, M23, M31, M32, M33, M41, M42, M43).Determinant().Multiply(M41).Negate()), (new Matrix3x3<T>(M11, M12, M13, M31, M32, M33, M41, M42, M43).Determinant().Multiply(M42)), (new Matrix3x3<T>(M11, M12, M13, M21, M22, M23, M41, M42, M43).Determinant().Multiply(M43).Negate()), (new Matrix3x3<T>(M11, M12, M13, M21, M22, M23, M31, M32, M33).Determinant().Multiply(M44))).Divide(Determinant());

		}
		
		/// <summary>
		/// Calculates the transpose of this matrix.
		/// </summary>
		/// <returns>The transpose.</returns>
		public Matrix4x4<T> Transpose()
		{
			return new Matrix4x4<T>(M11, M21, M31, M41, M12, M22, M32, M42, M13, M23, M33, M43, M14, M24, M34, M44);
		}
		
		/// <summary>
		/// Casts this matrix to another matrix with a different element type.
		/// </summary>
		/// <returns>The result of the operation.</returns>
		public Matrix4x4<U> Cast<U>()
			where U : struct, IEquatable<U>, IComparable<U>
		{
			return new Matrix4x4<U>((U)Convert.ChangeType(M11, typeof(U)), (U)Convert.ChangeType(M12, typeof(U)), (U)Convert.ChangeType(M13, typeof(U)), (U)Convert.ChangeType(M14, typeof(U)), (U)Convert.ChangeType(M21, typeof(U)), (U)Convert.ChangeType(M22, typeof(U)), (U)Convert.ChangeType(M23, typeof(U)), (U)Convert.ChangeType(M24, typeof(U)), (U)Convert.ChangeType(M31, typeof(U)), (U)Convert.ChangeType(M32, typeof(U)), (U)Convert.ChangeType(M33, typeof(U)), (U)Convert.ChangeType(M34, typeof(U)), (U)Convert.ChangeType(M41, typeof(U)), (U)Convert.ChangeType(M42, typeof(U)), (U)Convert.ChangeType(M43, typeof(U)), (U)Convert.ChangeType(M44, typeof(U)));
		}

		/// <summary>
		/// Adds two matrices together.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix4x4<T> operator +(Matrix4x4<T> left, Matrix4x4<T> right)
		{
			return left.Add(right);
		}
		
		/// <summary>
		/// Subtracts one matrix from another.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix4x4<T> operator -(Matrix4x4<T> left, Matrix4x4<T> right)
		{
			return left.Subtract(right);
		}
		
		/// <summary>
		/// Multiplies two matrices together.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix4x4<T> operator *(Matrix4x4<T> left, Matrix4x4<T> right)
		{
			return left.Multiply(right);
		}
		
		/// <summary>
		/// Multiplies a matrix by a scalar.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix4x4<T> operator *(Matrix4x4<T> left, T right)
		{
			return left.Multiply(right);
		}
		
		/// <summary>
		/// Divides a matrix by another.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix4x4<T> operator /(Matrix4x4<T> left, Matrix4x4<T> right)
		{
			return left.Divide(right);
		}
		
		/// <summary>
		/// Divides a matrix by a scalar.
		/// </summary>
		/// <param name="left">The left side of the operation.</param>
		/// <param name="right">The right side of the operation.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix4x4<T> operator /(Matrix4x4<T> left, T right)
		{
			return left.Divide(right);
		}
		
		/// <summary>
		/// Negates a matrix.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>The result of the operation.</returns>
		public static Matrix4x4<T> operator -(Matrix4x4<T> value)
		{
			return value.Negate();
		}

		/// <summary>
		/// Determines if two matrices are equal.
		/// </summary>
		/// <param name="left">The left side of the comparison.</param>
		/// <param name="right">The right side of the comparison.</param>
		/// <returns>The result of the comparison.</returns>
		public static bool operator ==(Matrix4x4<T> left, Matrix4x4<T> right)
		{
			return left.Equals(right);
		}
		
		/// <summary>
		/// Determines if two matrices are not equal.
		/// </summary>
		/// <param name="left">The left side of the comparison.</param>
		/// <param name="right">The right side of the comparison.</param>
		/// <returns>The result of the comparison.</returns>
		public static bool operator !=(Matrix4x4<T> left, Matrix4x4<T> right)
		{
			return !left.Equals(right);
		}
		
		/// <summary>
		/// Indicates whether the current object is equal to another object of the same type.
		/// </summary>
		/// <param name="other">An object to compare with this object.</param>
		/// <returns>
		/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
		/// </returns>
		public bool Equals(Matrix4x4<T> other)
		{
			return M11.Equals(other.M11) && M12.Equals(other.M12) && M13.Equals(other.M13) && M14.Equals(other.M14) && M21.Equals(other.M21) && M22.Equals(other.M22) && M23.Equals(other.M23) && M24.Equals(other.M24) && M31.Equals(other.M31) && M32.Equals(other.M32) && M33.Equals(other.M33) && M34.Equals(other.M34) && M41.Equals(other.M41) && M42.Equals(other.M42) && M43.Equals(other.M43) && M44.Equals(other.M44);
		}
		
		/// <summary>
		/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
		/// <returns>
		///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		public override bool Equals(object obj)
		{
			return obj is Matrix4x4<T> ? Equals((Matrix4x4<T>)obj) : false;
		}
		
		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode()
		{
			return M11.GetHashCode() ^ M12.GetHashCode() ^ M13.GetHashCode() ^ M14.GetHashCode() ^ M21.GetHashCode() ^ M22.GetHashCode() ^ M23.GetHashCode() ^ M24.GetHashCode() ^ M31.GetHashCode() ^ M32.GetHashCode() ^ M33.GetHashCode() ^ M34.GetHashCode() ^ M41.GetHashCode() ^ M42.GetHashCode() ^ M43.GetHashCode() ^ M44.GetHashCode();
		}
		
		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public override string ToString()
		{
			return "M11: " + M11.ToString() + ", " + "M12: " + M12.ToString() + ", " + "M13: " + M13.ToString() + ", " + "M14: " + M14.ToString() + ", " + "M21: " + M21.ToString() + ", " + "M22: " + M22.ToString() + ", " + "M23: " + M23.ToString() + ", " + "M24: " + M24.ToString() + ", " + "M31: " + M31.ToString() + ", " + "M32: " + M32.ToString() + ", " + "M33: " + M33.ToString() + ", " + "M34: " + M34.ToString() + ", " + "M41: " + M41.ToString() + ", " + "M42: " + M42.ToString() + ", " + "M43: " + M43.ToString() + ", " + "M44: " + M44.ToString();
		}
	}
}
